package com.easylinkin.linkappapi.building.service.impl;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.easylinkin.linkappapi.building.dto.BuildingDTO;
import com.easylinkin.linkappapi.building.entity.Building;
import com.easylinkin.linkappapi.building.entity.Floor;
import com.easylinkin.linkappapi.building.entity.vo.BuildingVo;
import com.easylinkin.linkappapi.building.mapper.BuildingMapper;
import com.easylinkin.linkappapi.building.service.BuildingService;
import com.easylinkin.linkappapi.building.service.FloorService;
import com.easylinkin.linkappapi.building.vo.BuildingFloorVO;
import com.easylinkin.linkappapi.common.exceptions.BusinessException;
import com.easylinkin.linkappapi.common.service.CommonService;
import com.easylinkin.linkappapi.common.utils.excel.ExcelConstant;
import com.easylinkin.linkappapi.common.utils.excel.ExcelTools;
import com.easylinkin.linkappapi.common.utils.io.OutputStreamUtil;
import com.easylinkin.linkappapi.machinery.entity.MachineryRecord;
import com.easylinkin.linkappapi.machinery.service.ElevatorFloorRecordService;
import com.easylinkin.linkappapi.machinery.service.MachineryRecordService;
import com.easylinkin.linkappapi.security.context.LinkappUserContextProducer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * Building表服务实现类
 *
 * @author CodeGenerator
 * @date 2022/09/21
 */
@Slf4j
@Service("appBuildingService")
public class BuildingServiceImpl extends ServiceImpl
        <BuildingMapper, Building> implements BuildingService {
    @Resource
    private CommonService commonService;
    @Resource
    private LinkappUserContextProducer linkappUserContextProducer;
    @Resource
    private FloorService floorService;
    @Resource
    private MachineryRecordService machineryRecordService;
    @Resource
    private ElevatorFloorRecordService elevatorFloorRecordService;

    @Autowired
    private BuildingMapper buildingMapper;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveOne(BuildingDTO appBuilding) {
        appBuilding.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        validParamRequired(appBuilding);
        // 重复性校验
        checkExist(appBuilding);
        validParamFormat(appBuilding);
        commonService.setCreateAndModifyInfo(appBuilding);
        // 设置坐标json
        if (null != appBuilding.getRegionDTOList() && appBuilding.getRegionDTOList().size() > 0) {
            String jsonStr = JSONUtil.toJsonStr(appBuilding.getRegionDTOList());
            appBuilding.setCoordinate(jsonStr);
        }
        boolean result = save(appBuilding);
        if (result) {
            floorService.initFloorForBuilding(appBuilding.getId());
        } else {
            throw new BusinessException("创建单体失败");
        }
        return true;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateOne(BuildingDTO appBuilding) {
        Assert.notNull(appBuilding.getId(), "id不能为空");
        commonService.setModifyInfo(appBuilding);
        appBuilding.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        // 重复性校验
        checkExist(appBuilding);
        validParamFormat(appBuilding);
        // 设置坐标json
        if (null != appBuilding.getRegionDTOList() && appBuilding.getRegionDTOList().size() > 0) {
            String jsonStr = JSONUtil.toJsonStr(appBuilding.getRegionDTOList());
            appBuilding.setCoordinate(jsonStr);
        }
        return updateById(appBuilding);
    }

    @Override
    public IPage<Building> selectPage(Page page, Building appBuilding) {
        appBuilding.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        return baseMapper.selectPage(page, appBuilding);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByIds(List<Long> idList) {
        if (ObjectUtils.isEmpty(idList)) {
            return true;
        }
        LambdaQueryWrapper<MachineryRecord> machineryRecordLambdaQueryWrapper = new LambdaQueryWrapper<>();
        machineryRecordLambdaQueryWrapper.in(MachineryRecord::getBuildingId, idList);
        Assert.isTrue(machineryRecordService.count(machineryRecordLambdaQueryWrapper) == 0, "该单体信息已被机械电子台账引用，如需删除，需解除关联");
//        查询单体下楼层
        LambdaQueryWrapper<Floor> floorLambdaQueryWrapper = new LambdaQueryWrapper<>();
        floorLambdaQueryWrapper.in(Floor::getBuildingId, idList).select(Floor::getId);
        List<Floor> floors = floorService.list(floorLambdaQueryWrapper);
        if (ObjectUtils.isNotEmpty(floors)) {
            floorService.checkBeUsed(floors.stream().map(Floor::getId).collect(Collectors.toList()));
        }

        boolean result = removeByIds(idList);
//        同时删除首层，基础层
        if (result) {
            floorLambdaQueryWrapper = new LambdaQueryWrapper<>();
            floorLambdaQueryWrapper.in(Floor::getBuildingId, idList);
            floorLambdaQueryWrapper.in(Floor::getType, 1, 0);
            floorService.remove(floorLambdaQueryWrapper);
        }
        return true;
    }

    @Override
    public void export(Building appBuilding, HttpServletRequest request, HttpServletResponse
            response) {

        IPage<Building> page = selectPage(new Page(0, -1), appBuilding);
        List<Building> records = page.getRecords();
        List
                <BuildingVo> appBuildingVos = new ArrayList<>();
        for (Building expert : records) {
            appBuildingVos.add(new BuildingVo(expert));
        }

        String keyValue = "名称:name,租户id:tenantId";
        String title = "Building导出数据";
        String fileName = title + ".xls";
        try {
            OutputStream outputStream = OutputStreamUtil
                    .getOutputStream(request, response, fileName);
            ExcelTools.exportExcel(outputStream, keyValue, appBuildingVos, ExcelConstant.XLS, title);
            response.flushBuffer();
            outputStream.close();
        } catch (IOException e) {
            log.error("excel导出失败", e);
            throw new RuntimeException("excel导出失败！IOException异常" + e.getMessage());
        } catch (Exception e) {
            log.error("excel导出失败", e);
            throw new RuntimeException("excel导出失败！" + e.getMessage());
        }
    }

    @Override
    public Building getOneById(Serializable id) {
        return baseMapper.getOneById(id);
    }

    /**
     * 校验重复
     */
    private void validRepeat(Building appBuilding) {
        QueryWrapper<Building> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("name", appBuilding.getName());
        queryWrapper.eq("tenant_id", linkappUserContextProducer.getNotNullCurrent().getTenantId());
        List<Building> list = baseMapper.selectList(queryWrapper);
        if (list.size() == 0) {
            return;
        }
        if (list.size() > 1) {
            throw new BusinessException("名称有重复");
        }
        if (ObjectUtils.isEmpty(appBuilding.getId())) {
            throw new BusinessException("名称已存在");
        }
        if (!appBuilding.getId().equals(list.get(0).getId())) {
            throw new BusinessException("名称已存在");
        }
    }


    /**
     * 校验参数必填
     */
    private void validParamRequired(Building appBuilding) {
        Assert.notNull(appBuilding, "参数为空");
        Assert.isTrue(ObjectUtils.isNotEmpty(appBuilding.getName()), "名称为空");
    }

    /**
     * 校验参数格式
     */
    private void validParamFormat(Building appBuilding) {
        Assert.isTrue(appBuilding.getName() == null || appBuilding.getName().length() <= 30,
                "名称超长");
    }

    @Override
    public Building findBuildingWithFloors(Integer id) {
        Building build = baseMapper.selectById(id);
        if (build != null) {
            LambdaQueryWrapper<Floor> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(Floor::getBuildingId, id);
            List<Floor> floors = floorService.list(lambdaQueryWrapper);
            build.setFloorList(floors);
        }
        return build;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean copy(Building appBuilding) {
        Assert.notNull(appBuilding.getId(), "单体id不能为空");
        Building build = getOneById(appBuilding.getId());
        Assert.notNull(build, "查询不到该单体");
        LambdaQueryWrapper<Floor> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Floor::getBuildingId, appBuilding.getId());
        List<Floor> floors = floorService.list(lambdaQueryWrapper);
        build.setId(null);
        build.setName(build.getName() + "-副本");

        validParamRequired(build);
        validRepeat(build);
        validParamFormat(build);
        commonService.setCreateAndModifyInfo(build);
        save(build);

        for (Floor floor : floors) {
            floor.setBuildingId(build.getId());
            floor.setId(null);
        }
        return floorService.saveBatch(floors);
    }

    @Override
    public List<BuildingFloorVO> getTree() {
        return buildingMapper.getTree(linkappUserContextProducer.getNotNullCurrent().getTenantId());
    }

    @Override
    public List<BuildingFloorVO> getTreeWithNum() {
        List<BuildingFloorVO> buildingFloorVOS = buildingMapper.getTreeWithNum(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        //计算单体下的人数
        for (BuildingFloorVO vo : buildingFloorVOS) {
            int num = 0;
            for (Floor floor : vo.getFloors()) {
                if (null != floor) {
                    num += floor.getNum();
                }
            }
            vo.setNum(num);
        }
        return buildingFloorVOS;
    }

    /**
     * 验证重复
     */
    private void checkExist(BuildingDTO buildingDTO) {
        String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
        QueryWrapper<Building> queryWrapper = new QueryWrapper<>();
        //设置判断重复条件
        queryWrapper.eq("name", buildingDTO.getName())
                .eq("tenant_id", tenantId);
        //编辑的时候存在id
        Optional.ofNullable(buildingDTO.getId()).ifPresent(id -> queryWrapper.ne("id", buildingDTO.getId()));
        Integer integer = baseMapper.selectCount(queryWrapper);
        if (integer > 0){
            throw new BusinessException("单体名称已存在");
        }
    }
}

